﻿
namespace HSS.ComponentModel
{
	#region Using Directives
	using System;
	using System.Collections;
	using System.Collections.Generic;
	using System.Threading;
	using HSS.Resources;
	#endregion

	#region PropertyDescriptorCollection
	/// <summary>
	/// Represents a collection of PropertyDescriptor objects.
	/// </summary>
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
	public sealed class PropertyDescriptorCollection : IList, IDictionary, ICollection, IEnumerable<HSSPropertyDescriptor>
	{
		#region Constants
		const string INDEX_NAME = "index";
		const string KEY_NAME = "key";
		const string VALUE_NAME = "value";
		#endregion

		#region Fields
		/// <summary>
		/// Specifies an empty collection that you can use instead of creating a new one with no items. This static field is read-only.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
		public static readonly PropertyDescriptorCollection Empty = new PropertyDescriptorCollection(null, true);

		private IDictionary<object, object> cachedFoundProperties;
		private bool cachedIgnoreCase;
		private IComparer<HSSPropertyDescriptor> defaultComparer;
		private string[] namedSort;
		private bool needSort;
		private int propCount;
		private HSSPropertyDescriptor[] properties;
		private bool propsOwned;
		private bool readOnly;

		#endregion

		#region Constructors
		/// <summary>
		/// Initializes a new instance of the PropertyDescriptorCollection class.
		/// </summary>
		/// <param name="properties">An array of type PropertyDescriptor that provides the properties for this collection.</param>
		public PropertyDescriptorCollection(HSSPropertyDescriptor[] properties)
		{
			this.propsOwned = true;
			this.properties = properties;
			if (properties == null)
			{
				this.properties = new HSSPropertyDescriptor[0];
				this.propCount = 0;
			}
			else
			{
				this.propCount = properties.Length;
			}
			this.propsOwned = true;
		}
		/// <summary>
		/// Initializes a new instance of the PropertyDescriptorCollection class, which is optionally read-only.
		/// </summary>
		/// <param name="properties">An array of type PropertyDescriptor that provides the properties for this collection.</param>
		/// <param name="readOnly">If true, specifies that the collection cannot be modified.</param>
		public PropertyDescriptorCollection(HSSPropertyDescriptor[] properties, bool readOnly)
			: this(properties)
		{
			this.readOnly = readOnly;
		}
		private PropertyDescriptorCollection(HSSPropertyDescriptor[] properties, int propCount, string[] namedSort, IComparer<HSSPropertyDescriptor> comparer)
		{
			this.propsOwned = true;
			this.propsOwned = false;
			if (namedSort != null)
			{
				this.namedSort = (string[])namedSort.Clone();
			}
			this.defaultComparer = comparer;
			this.properties = properties;
			this.propCount = propCount;
			this.needSort = true;
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets the number of property descriptors in the collection.
		/// </summary>
		public int Count
		{
			get
			{
				return this.propCount;
			}
		}
		/// <summary>
		/// Gets or sets the PropertyDescriptor with the specified name.
		/// </summary>
		/// <param name="name">The name of the PropertyDescriptor to get from the collection.</param>
		/// <returns>The PropertyDescriptor with the specified name, or null if the property does not exist.</returns>
		public HSSPropertyDescriptor this[string name]
		{
			get
			{
				return this.Find(name, false);
			}
		}
		/// <summary>
		/// Gets or sets the PropertyDescriptor at the specified index number.
		/// </summary>
		/// <param name="index">The zero-based index of the PropertyDescriptor to get or set.</param>
		/// <returns>The PropertyDescriptor with the specified index number.</returns>
		public HSSPropertyDescriptor this[int index]
		{
			get
			{
				return this.FromIndex(index);
			}
		}
		private HSSPropertyDescriptor FromIndex(int index)
		{
			if (index >= this.propCount)
				throw new ArgumentException(ResourceProvider.GetString(StringKeys.PropertyDescriptorCollection_IndexedError), INDEX_NAME);
			this.EnsurePropsOwned();
			return this.properties[index];
		}

		/// <summary>
		/// Gets the number of elements contained in the collection.
		/// </summary>
		int ICollection.Count
		{
			get
			{
				return this.Count;
			}
		}
		/// <summary>
		/// Gets a value indicating whether access to the collection is synchronized (thread safe).
		/// </summary>
		bool ICollection.IsSynchronized
		{
			get
			{
				return false;
			}
		}
		/// <summary>
		/// Gets an object that can be used to synchronize access to the collection.
		/// </summary>
		object ICollection.SyncRoot
		{
			get
			{
				return null;
			}
		}
		/// <summary>
		/// Gets a value indicating whether the IDictionary has a fixed size.
		/// </summary>
		bool IDictionary.IsFixedSize
		{
			get
			{
				return this.readOnly;
			}
		}
		/// <summary>
		/// Gets a value indicating whether the IDictionary is read-only.
		/// </summary>
		bool IDictionary.IsReadOnly
		{
			get
			{
				return this.readOnly;
			}
		}
		/// <summary>
		/// Gets or sets the element with the specified key.
		/// </summary>
		/// <param name="key">The key of the element to get or set.</param>
		/// <returns>The element with the specified key.</returns>
		object IDictionary.this[object key]
		{
			get
			{
				var name = key as string;
				if (!string.IsNullOrWhiteSpace(name))
					return this.Find(name, false);
				return null;
			}
			set
			{
				if (this.readOnly)
					throw new NotSupportedException();
				var v = value as HSSPropertyDescriptor;
				if (v == null)
					throw new ArgumentException("value");

				var index = -1;
				var ks = key as string;
				if (key is int)
				{
					index = (int)key;
					if ((index < 0) || (index >= this.propCount))
						throw new IndexOutOfRangeException();
				}
				else
				{
					if (null == ks)
						throw new ArgumentException(KEY_NAME);

					for (int i = 0; i < this.propCount; i++)
					{
						if (this.properties[i].Name.Equals(ks))
						{
							index = i;
							break;
						}
					}
				}
				if (index == -1)
				{
					this.Add(v);
				}
				else
				{
					this.EnsurePropsOwned();
					this.properties[index] = v;
					if ((this.cachedFoundProperties != null) && (null != ks))
						this.cachedFoundProperties[key] = value;
				}
			}
		}
		/// <summary>
		/// Gets an ICollection containing the keys of the IDictionary.
		/// </summary>
		ICollection IDictionary.Keys
		{
			get
			{
				string[] strArray = new string[this.propCount];
				for (int i = 0; i < this.propCount; i++)
				{
					strArray[i] = this.properties[i].Name;
				}
				return strArray;
			}
		}
		/// <summary>
		/// Gets an ICollection containing the values in the IDictionary.
		/// </summary>
		ICollection IDictionary.Values
		{
			get
			{
				if (this.properties.Length != this.propCount)
				{
					var destinationArray = new HSSPropertyDescriptor[this.propCount];
					Array.Copy(this.properties, 0, destinationArray, 0, this.propCount);
					return destinationArray;
				}
				return (ICollection)this.properties.Clone();
			}
		}
		/// <summary>
		/// Gets a value indicating whether the collection has a fixed size.
		/// </summary>
		bool IList.IsFixedSize
		{
			get
			{
				return this.readOnly;
			}
		}
		/// <summary>
		/// Gets a value indicating whether the collection is read-only.
		/// </summary>
		bool IList.IsReadOnly
		{
			get
			{
				return this.readOnly;
			}
		}
		/// <summary>
		/// Gets or sets an item from the collection at a specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the item to get or set.</param>
		/// <returns>The element at the specified index.</returns>
		object IList.this[int index]
		{
			get
			{
				return this.FromIndex(index);
			}
			set
			{
				if (this.readOnly)
				{
					throw new NotSupportedException();
				}
				if (index >= this.propCount)
				{
					throw new ArgumentException();
				}
				var v = value as HSSPropertyDescriptor;
				if (v == null)
					throw new ArgumentException("value");
				this.EnsurePropsOwned();
				this.properties[index] = v;
			}
		}
		#endregion

		#region Methods
		/// <summary>
		/// Adds the specified PropertyDescriptor to the collection.
		/// </summary>
		/// <param name="value">The PropertyDescriptor to add to the collection.</param>
		/// <returns>The index of the PropertyDescriptor that was added to the collection.</returns>
		public int Add(HSSPropertyDescriptor value)
		{
			if (this.readOnly)
			{
				throw new NotSupportedException();
			}
			this.EnsureSize(this.propCount + 1);
			this.properties[this.propCount++] = value;
			return (this.propCount - 1);
		}
		/// <summary>
		/// Removes all PropertyDescriptor objects from the collection.
		/// </summary>
		public void Clear()
		{
			if (this.readOnly)
			{
				throw new NotSupportedException();
			}
			this.propCount = 0;
			this.cachedFoundProperties = null;
		}
		/// <summary>
		/// Returns whether the collection contains the given PropertyDescriptor.
		/// </summary>
		/// <param name="value">The PropertyDescriptor to find in the collection.</param>
		/// <returns>true if the collection contains the given PropertyDescriptor; otherwise, false.</returns>
		public bool Contains(HSSPropertyDescriptor value)
		{
			return (this.IndexOf(value) >= 0);
		}
		/// <summary>
		/// Copies the entire collection to an array, starting at the specified index number.
		/// </summary>
		/// <param name="array">An array of PropertyDescriptor objects to copy elements of the collection to.</param>
		/// <param name="index">The index of the array parameter at which copying begins.</param>
		void ICollection.CopyTo(Array array, int index)
		{
			this.EnsurePropsOwned();
			Array.Copy(this.properties, 0, array, index, this.Count);
		}
		/// <summary>
		/// Copies the entire collection to an array, starting at the specified index number.
		/// </summary>
		/// <param name="array">An array of PropertyDescriptor objects to copy elements of the collection to.</param>
		/// <param name="index">The index of the array parameter at which copying begins.</param>
		public void CopyTo(HSSPropertyDescriptor[] array, int index)
		{
			((ICollection)this).CopyTo(array, index);
		}
		/// <summary>
		/// Returns the PropertyDescriptor with the specified name, using a Boolean to indicate whether to ignore case.
		/// </summary>
		/// <param name="name">The name of the PropertyDescriptor to return from the collection.</param>
		/// <param name="ignoreCase">true if you want to ignore the case of the property name; otherwise, false.</param>
		/// <returns>A PropertyDescriptor with the specified name, or null if the property does not exist.</returns>
		public HSSPropertyDescriptor Find(string name, bool ignoreCase)
		{
			HSSPropertyDescriptor descriptor2;
			PropertyDescriptorCollection descriptors = null;
			bool lockTaken = false;
			try
			{
				Monitor.Enter(descriptors = this, ref lockTaken);
				HSSPropertyDescriptor descriptor = null;
				if ((this.cachedFoundProperties == null) || (this.cachedIgnoreCase != ignoreCase))
				{
					this.cachedIgnoreCase = ignoreCase;
					this.cachedFoundProperties = new Dictionary<object, object>();
				}
				object obj2 = this.cachedFoundProperties[name];
				if (obj2 != null)
				{
					return (HSSPropertyDescriptor)obj2;
				}
				for (int i = 0; i < this.propCount; i++)
				{
					if (ignoreCase)
					{
						if (!string.Equals(this.properties[i].Name, name, StringComparison.OrdinalIgnoreCase))
						{
							continue;
						}
						this.cachedFoundProperties[name] = this.properties[i];
						descriptor = this.properties[i];
						break;
					}
					if (this.properties[i].Name.Equals(name))
					{
						this.cachedFoundProperties[name] = this.properties[i];
						descriptor = this.properties[i];
						break;
					}
				}
				descriptor2 = descriptor;
			}
			finally
			{
				if (lockTaken)
				{
					Monitor.Exit(descriptors);
				}
			}
			return descriptor2;
		}
		/// <summary>
		/// Returns an enumerator for this class.
		/// </summary>
		/// <returns>An enumerator of type IEnumerator.</returns>
		public IEnumerator<HSSPropertyDescriptor> GetEnumerator()
		{
			this.EnsurePropsOwned();
			if (this.properties.Length != this.propCount)
			{
				HSSPropertyDescriptor[] destinationArray = new HSSPropertyDescriptor[this.propCount];
				Array.Copy(this.properties, 0, destinationArray, 0, this.propCount);
				return new List<HSSPropertyDescriptor>(destinationArray).GetEnumerator();
			}
			return new List<HSSPropertyDescriptor>(this.properties).GetEnumerator();
		}
		/// <summary>
		/// Returns the index of the given PropertyDescriptor.
		/// </summary>
		/// <param name="value">The PropertyDescriptor to return the index of.</param>
		/// <returns>The index of the given PropertyDescriptor.</returns>
		public int IndexOf(HSSPropertyDescriptor value)
		{
			return Array.IndexOf<HSSPropertyDescriptor>(this.properties, value, 0, this.propCount);
		}
		/// <summary>
		/// Adds the PropertyDescriptor to the collection at the specified index number.
		/// </summary>
		/// <param name="index">The index at which to add the value parameter to the collection.</param>
		/// <param name="value">The PropertyDescriptor to add to the collection.</param>
		public void Insert(int index, HSSPropertyDescriptor value)
		{
			if (this.readOnly)
			{
				throw new NotSupportedException();
			}
			this.EnsureSize(this.propCount + 1);
			if (index < this.propCount)
			{
				Array.Copy(this.properties, index, this.properties, index + 1, this.propCount - index);
			}
			this.properties[index] = value;
			this.propCount++;
		}
		/// <summary>
		/// Sorts the members of this collection, using the specified IComparer.
		/// </summary>
		/// <param name="sorter">A comparer to use to sort the PropertyDescriptor objects in this collection.</param>
		private void InternalSort(IComparer<HSSPropertyDescriptor> sorter)
		{
			if (sorter == null)
			{
				TypeDescriptor.SortDescriptorArray(this.properties);
			}
			else
			{
				Array.Sort(this.properties, sorter);
			}
		}
		/// <summary>
		/// Sorts the members of this collection. The specified order is applied first, followed by the default sort for this collection, which is usually alphabetical.
		/// </summary>
		/// <param name="names">An array of strings describing the order in which to sort the PropertyDescriptor objects in this collection.</param>
		private void InternalSort(string[] names)
		{
			if ((this.properties != null) && (this.properties.Length != 0))
			{
				this.InternalSort(this.defaultComparer);
				if ((names != null) && (names.Length > 0))
				{
					List<HSSPropertyDescriptor> list = new List<HSSPropertyDescriptor>(this.properties);
					int num = 0;
					int length = this.properties.Length;
					for (int i = 0; i < names.Length; i++)
					{
						for (int k = 0; k < length; k++)
						{
							var descriptor = (HSSPropertyDescriptor)list[k];
							if ((descriptor != null) && descriptor.Name.Equals(names[i]))
							{
								this.properties[num++] = descriptor;
								list[k] = null;
								goto Label_0096;
							}
						}
					Label_0096: ;
					}
					for (int j = 0; j < length; j++)
					{
						if (list[j] != null)
						{
							this.properties[num++] = (HSSPropertyDescriptor)list[j];
						}
					}
				}
			}
		}
		/// <summary>
		/// Removes the specified PropertyDescriptor from the collection.
		/// </summary>
		/// <param name="value">The PropertyDescriptor to remove from the collection.</param>
		public void Remove(HSSPropertyDescriptor value)
		{
			if (this.readOnly)
				throw new NotSupportedException();
			var index = this.IndexOf(value);
			if (index != -1)
				this.RemoveAt(index);
		}
		/// <summary>
		/// Removes the PropertyDescriptor at the specified index from the collection.
		/// </summary>
		/// <param name="index">The index of the PropertyDescriptor to remove from the collection.</param>
		public void RemoveAt(int index)
		{
			if (this.readOnly)
			{
				throw new NotSupportedException();
			}
			if (index < (this.propCount - 1))
			{
				Array.Copy(this.properties, index + 1, this.properties, index, (this.propCount - index) - 1);
			}
			this.properties[this.propCount - 1] = null;
			this.propCount--;
		}
		/// <summary>
		/// Sorts the members of this collection, using the default sort for this collection, which is usually alphabetical.
		/// </summary>
		/// <returns>A new PropertyDescriptorCollection that contains the sorted PropertyDescriptor objects.</returns>
		public PropertyDescriptorCollection Sort()
		{
			return new PropertyDescriptorCollection(this.properties, this.propCount, this.namedSort, this.defaultComparer);
		}
		/// <summary>
		/// Sorts the members of this collection, using the specified IComparer.
		/// </summary>
		/// <param name="comparer">A comparer to use to sort the PropertyDescriptor objects in this collection.</param>
		/// <returns>A new PropertyDescriptorCollection that contains the sorted PropertyDescriptor objects.</returns>
		public PropertyDescriptorCollection Sort(IComparer<HSSPropertyDescriptor> comparer)
		{
			return new PropertyDescriptorCollection(this.properties, this.propCount, this.namedSort, comparer);
		}
		/// <summary>
		/// Sorts the members of this collection. The specified order is applied first, followed by the default sort for this collection, which is usually alphabetical.
		/// </summary>
		/// <param name="names">An array of strings describing the order in which to sort the PropertyDescriptor objects in this collection.</param>
		/// <returns>A new PropertyDescriptorCollection that contains the sorted PropertyDescriptor objects.</returns>
		public PropertyDescriptorCollection Sort(string[] names)
		{
			return new PropertyDescriptorCollection(this.properties, this.propCount, names, this.defaultComparer);
		}
		/// <summary>
		/// Sorts the members of this collection. The specified order is applied first, followed by the sort using the specified IComparer.
		/// </summary>
		/// <param name="names">An array of strings describing the order in which to sort the PropertyDescriptor objects in this collection.</param>
		/// <param name="comparer">A comparer to use to sort the PropertyDescriptor objects in this collection.</param>
		/// <returns>A new PropertyDescriptorCollection that contains the sorted PropertyDescriptor objects.</returns>
		public PropertyDescriptorCollection Sort(string[] names, IComparer<HSSPropertyDescriptor> comparer)
		{
			return new PropertyDescriptorCollection(this.properties, this.propCount, names, comparer);
		}

		private void EnsurePropsOwned()
		{
			if (!this.propsOwned)
			{
				this.propsOwned = true;
				if (this.properties != null)
				{
					var destinationArray = new HSSPropertyDescriptor[this.Count];
					Array.Copy(this.properties, 0, destinationArray, 0, this.Count);
					this.properties = destinationArray;
				}
			}
			if (this.needSort)
			{
				this.needSort = false;
				this.InternalSort(this.namedSort);
			}
		}
		private void EnsureSize(int sizeNeeded)
		{
			if (sizeNeeded > this.properties.Length)
			{
				if ((this.properties == null) || (this.properties.Length == 0))
				{
					this.propCount = 0;
					this.properties = new HSSPropertyDescriptor[sizeNeeded];
				}
				else
				{
					this.EnsurePropsOwned();
					var destinationArray = new HSSPropertyDescriptor[Math.Max(sizeNeeded, this.properties.Length * 2)];
					Array.Copy(this.properties, 0, destinationArray, 0, this.propCount);
					this.properties = destinationArray;
				}
			}
		}


		/// <summary>
		/// Adds an element with the provided key and value to the IDictionary.
		/// </summary>
		/// <param name="key">The Object to use as the key of the element to add.</param>
		/// <param name="value">The Object to use as the value of the element to add.</param>
		void IDictionary.Add(object key, object value)
		{
			var descriptor = value as HSSPropertyDescriptor;
			if (descriptor == null)
				throw new ArgumentException(VALUE_NAME);
			this.Add(descriptor);
		}
		/// <summary>
		/// Removes all elements from the IDictionary.
		/// </summary>
		void IDictionary.Clear()
		{
			this.Clear();
		}
		/// <summary>
		/// Determines whether the IDictionary contains an element with the specified key.
		/// </summary>
		/// <param name="key">The key to locate in the IDictionary.</param>
		/// <returns>true if the IDictionary contains an element with the key; otherwise, false.</returns>
		bool IDictionary.Contains(object key)
		{
			var ks = key as string;
			return (null != ks && this[ks] != null);
		}
		/// <summary>
		/// Returns an enumerator for this class.
		/// </summary>
		/// <returns>An enumerator of type IEnumerator.</returns>
		IDictionaryEnumerator IDictionary.GetEnumerator()
		{
			return new PropertyDescriptorEnumerator(this);
		}
		/// <summary>
		/// Removes the element with the specified key from the IDictionary.
		/// </summary>
		/// <param name="key">The key of the element to remove.</param>
		void IDictionary.Remove(object key)
		{
			var ks = key as string;
			if (null != ks)
			{
				var descriptor = this[ks];
				if (descriptor != null)
					((IList)this).Remove(descriptor);
			}
		}
		/// <summary>
		/// Returns an IEnumerator for the IDictionary.
		/// </summary>
		/// <returns>An IEnumerator for the IDictionary.</returns>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}
		/// <summary>
		/// Returns an IEnumerator of type <see cref="HSSPropertyDescriptor"/> for the IDictionary.
		/// </summary>
		/// <returns>An IEnumerator of type <see cref="HSSPropertyDescriptor"/> for the IDictionary.</returns>
		IEnumerator<HSSPropertyDescriptor> IEnumerable<HSSPropertyDescriptor>.GetEnumerator()
		{
			return this.GetEnumerator();
		}
		/// <summary>
		/// Adds an item to the IList.
		/// </summary>
		/// <param name="value">The item to add to the collection.</param>
		/// <returns>The position into which the new element was inserted.</returns>
		int IList.Add(object value)
		{
			return this.Add((HSSPropertyDescriptor)value);
		}
		/// <summary>
		/// Removes all items from the collection.
		/// </summary>
		void IList.Clear()
		{
			this.Clear();
		}
		/// <summary>
		/// Determines whether the collection contains a specific value.
		/// </summary>
		/// <param name="value">The item to locate in the collection.</param>
		/// <returns>true if the item is found in the collection; otherwise, false.</returns>
		bool IList.Contains(object value)
		{
			return this.Contains((HSSPropertyDescriptor)value);
		}
		/// <summary>
		/// Determines the index of a specified item in the collection.
		/// </summary>
		/// <param name="value">The item to locate in the collection.</param>
		/// <returns>The index of value if found in the list, otherwise -1.</returns>
		int IList.IndexOf(object value)
		{
			return this.IndexOf((HSSPropertyDescriptor)value);
		}
		/// <summary>
		/// Inserts an item into the collection at a specified index.
		/// </summary>
		/// <param name="index">The zero-based index at which value should be inserted.</param>
		/// <param name="value">The item to insert into the collection.</param>
		void IList.Insert(int index, object value)
		{
			this.Insert(index, (HSSPropertyDescriptor)value);
		}
		/// <summary>
		/// Removes the first occurrence of a specified value from the collection.
		/// </summary>
		/// <param name="value">The item to remove from the collection.</param>
		void IList.Remove(object value)
		{
			this.Remove((HSSPropertyDescriptor)value);
		}
		/// <summary>
		/// Removes the item at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the item to remove.</param>
		void IList.RemoveAt(int index)
		{
			this.RemoveAt(index);
		}
		#endregion

		#region PropertyDescriptorEnumerator
		private class PropertyDescriptorEnumerator : IDictionaryEnumerator, IEnumerator
		{
			private int index = -1;
			private PropertyDescriptorCollection owner;

			public PropertyDescriptorEnumerator(PropertyDescriptorCollection owner)
			{
				this.owner = owner;
			}

			public bool MoveNext()
			{
				if (this.index < (this.owner.Count - 1))
				{
					this.index++;
					return true;
				}
				return false;
			}

			public void Reset()
			{
				this.index = -1;
			}

			public object Current
			{
				get
				{
					return this.Entry;
				}
			}

			public DictionaryEntry Entry
			{
				get
				{
					HSSPropertyDescriptor descriptor = this.owner[this.index];
					return new DictionaryEntry(descriptor.Name, descriptor);
				}
			}

			public object Key
			{
				get
				{
					return this.owner[this.index].Name;
				}
			}

			public object Value
			{
				get
				{
					return this.owner[this.index].Name;
				}
			}
		}
		#endregion
	}
	#endregion
}